package services

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"log"
	"os"
	"path"
	"path/filepath"
	"server/global"
	"strings"
	"time"

	asr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/asr/v20190614"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"

	txError "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
)

// WordResult 是识别结果中每个词的结构
type TencentCloudResponse struct {
	Response struct {
		Data struct {
			TaskId        uint64         `json:"TaskId"`
			Status        int            `json:"Status"`
			StatusStr     string         `json:"StatusStr"`
			Result        string         `json:"Result"`
			ErrorMsg      string         `json:"ErrorMsg"`
			ResultDetail  []ResultDetail `json:"ResultDetail"`
			AudioDuration float64        `json:"AudioDuration"`
		} `json:"Data"`
		RequestId string `json:"RequestId"`
	} `json:"Response"`
}

type ResultDetail struct {
	FinalSentence   string       `json:"FinalSentence"`
	SliceSentence   string       `json:"SliceSentence"`
	WrittenText     string       `json:"WrittenText"`
	StartMs         int64        `json:"StartMs"`
	EndMs           int64        `json:"EndMs"`
	WordsNum        int          `json:"WordsNum"`
	Words           []WordResult `json:"Words"`
	SpeechSpeed     float64      `json:"SpeechSpeed"`
	SpeakerId       int          `json:"SpeakerId"`
	EmotionalEnergy int          `json:"EmotionalEnergy"`
	SilenceTime     int          `json:"SilenceTime"`
	EmotionType     []string     `json:"EmotionType"`
}

type WordResult struct {
	Word          string `json:"Word"`
	OffsetStartMs int64  `json:"OffsetStartMs"`
	OffsetEndMs   int64  `json:"OffsetEndMs"`
}

// 语音识别
func (mediaService *mediaService) SpeechRecognition(audioLocalPath string) (string, error) {
	// storage := global.App.Config.Storage.Disks.Local.RootDir + "/"
	// storage +
	audioPath, _ := filepath.Abs(audioLocalPath)

	secretKey := global.App.Config.Tencent.SecretKey
	secretId := global.App.Config.Tencent.SecretId
	regions := global.App.Config.Tencent.Regions

	// 初始化认证信息
	credential := common.NewCredential(secretId, secretKey)

	// 实例化一个客户端配置对象
	cpf := profile.NewClientProfile()

	// 实例化要请求产品的client对象
	client, _ := asr.NewClient(credential, regions, cpf)

	// 实例化一个请求对象
	// request := asr.NewSentenceRecognitionRequest()
	request := asr.NewCreateRecTaskRequest()
	// 读取音频文件内容
	data, err := os.ReadFile(audioPath)
	if err != nil {
		panic(err)
	}

	// 配置请求参数
	/* 	request.ProjectId = common.Uint64Ptr(0)                                  // 项目ID，0为默认项目
	   	request.SubServiceType = common.Uint64Ptr(2)                             // 子服务类型，2表示 16k 语音识别
	   	request.EngSerViceType = common.StringPtr("16k_zh")                      // 引擎模型类型
	   	request.SourceType = common.Uint64Ptr(1)                                 // 1表示语音数据是二进制数据
	   	request.VoiceFormat = common.StringPtr("mp3")                            // 音频格式，mp3、wav等
	   	request.UsrAudioKey = common.StringPtr("test")                           // 用户端对此任务的唯一标识
	   	request.Data = common.StringPtr(base64.StdEncoding.EncodeToString(data)) // 音频数据，需要使用base64编码
	*/
	// 16k_zh中文,16k_ja日语
	request.EngineModelType = common.StringPtr("16k_zh") // 引擎模型类型
	request.ChannelNum = common.Uint64Ptr(1)             // 声道数，根据音频文件的声道数设置
	request.ResTextFormat = common.Uint64Ptr(1)          // 返回的文本格式，0为json
	request.SourceType = common.Uint64Ptr(1)             // 1表示语音数据是来自URL的音频
	// request.CallbackUrl = common.StringPtr("") // 设置你的回调 URL
	request.Data = common.StringPtr(base64.StdEncoding.EncodeToString(data)) // 音频数据，需要使用base64编码
	// 通过client对象调用想要访问的接口
	// response, err := client.SentenceRecognition(request)
	response, err := client.CreateRecTask(request)
	if _, ok := err.(*txError.TencentCloudSDKError); ok {
		fmt.Printf("TencentCloudAPI回来错误信息: %s\n", err)
		return "", err
	}
	if err != nil {
		panic(err)
	}
	// 输出json格式的字符串回包
	// fmt.Printf("json格式的字符串回包:%s\n", response.ToJsonString())

	// if response != nil {
	// 	return response.Response.RequestId, nil
	// }
	// return "", nil

	//获取taskId

	taskId := response.Response.Data.TaskId
	//轮询获取结果
	return mediaService.PollingForResult(client, *taskId)
}

// PollingForResult 轮询获取识别结果
func (mediaService *mediaService) PollingForResult(client *asr.Client, taskId uint64) (string, error) {
	// 实例化一个查询请求对象
	request := asr.NewDescribeTaskStatusRequest()

	// 设置任务ID
	request.TaskId = common.Uint64Ptr(taskId)

	// 轮询间隔时间，根据实际情况调整
	pollInterval := 5 * time.Second

	for {
		// 调用查询接口
		response, err := client.DescribeTaskStatus(request)
		if _, ok := err.(*txError.TencentCloudSDKError); ok {
			return "", err
		}
		if err != nil {
			return "", err
		}
		fmt.Println("完整响应:", response.ToJsonString())

		// 检查任务状态
		status := response.Response.Data.Status
		switch *status {
		case 2: // 任务成功
			// 提取ResultDetail中的Words数组
			var words []WordResult
			//遍历取词
			for _, detail := range response.Response.Data.ResultDetail {
				for _, word := range detail.Words {
					// 确保指针不是nil，然后解引用
					var startMs, endMs int64
					var wordText string
					if word.OffsetStartMs != nil {
						startMs = *word.OffsetStartMs
					}
					if word.OffsetEndMs != nil {
						endMs = *word.OffsetEndMs
					}
					if word.Word != nil {
						wordText = *word.Word
					}
					words = append(words, WordResult{
						Word:          wordText,
						OffsetStartMs: startMs,
						OffsetEndMs:   endMs,
					})
				}
			}

			// 使用words数组生成字幕文件
			subtitleFileName, err := mediaService.GenerateCombinedSubtitleFile(words)
			if err != nil {
				return "", err
			}
			return subtitleFileName, nil
		case 3: // 任务失败
			return "", fmt.Errorf("任务失败，失败原因：%s", *response.Response.Data.ErrorMsg)
		case 1: // 任务执行中
			// 如果任务正在执行中，则等待一段时间后继续轮询
			time.Sleep(pollInterval)
			continue
		case 0: // 任务等待
			// 如果任务在等待中，则等待一段时间后继续轮询
			time.Sleep(pollInterval)
			continue
		default:
			return "", fmt.Errorf("未知的任务状态：%d", *status)
		}
	}
}

// parseTimestamp 解析时间戳，将形如 [0:0.100,0:55.641] 的时间戳转换为 srt 格式的时间码
/* func parseTimestamp(timestamp string) (string, string, error) {
	re := regexp.MustCompile(`\[(\d+):(\d+\.\d+),(\d+):(\d+\.\d+)\]`)
	matches := re.FindStringSubmatch(timestamp)
	if len(matches) != 5 {
		return "", "", fmt.Errorf("时间戳格式错误: %s", timestamp)
	}

	// 将时间戳转换为 srt 格式的时间码
	startMin, _ := strconv.Atoi(matches[1])
	startSec, _ := strconv.ParseFloat(matches[2], 64)
	endMin, _ := strconv.Atoi(matches[3])
	endSec, _ := strconv.ParseFloat(matches[4], 64)

	startTime := time.Duration(startMin)*time.Minute + time.Duration(startSec*float64(time.Second))
	endTime := time.Duration(endMin)*time.Minute + time.Duration(endSec*float64(time.Second))

	start := fmt.Sprintf("%02d:%02d:%02d,%03d", int(startTime.Hours()), int(startTime.Minutes())%60, int(startTime.Seconds())%60, startTime.Milliseconds()%1000)
	end := fmt.Sprintf("%02d:%02d:%02d,%03d", int(endTime.Hours()), int(endTime.Minutes())%60, int(endTime.Seconds())%60, endTime.Milliseconds()%1000)

	return start, end, nil
} */

// GenerateCombinedSubtitleFile 生成合并后的字幕文件，每条大约5秒
func (mediaService *mediaService) GenerateCombinedSubtitleFile(words []WordResult) (string, error) {
	//planA，五秒一句，但不全
	/* 	const maxDurationMs int64 = 5000 // 5秒的毫秒数
	   	currentTime := time.Now()

	   	subtitleDir := mediaService.makeFaceDir("subTitle")
	   	subtitleFileName := fmt.Sprintf("combined_subtitle_%s.srt", currentTime.Format("20060102_150405"))

	   	// 创建subTitle目录（如果它还不存在）
	   	fullPath := path.Join(subtitleDir, subtitleFileName)

	   	//  未检查subTitle路径是否存在 ，可能会报错
	   	// // Windows不支持Unix权限，所以只需设置为0 , Unix/Linux使用0755权限
	   	if err := os.MkdirAll(subtitleDir, 0); err != nil {
	   		return "", fmt.Errorf("创建字幕目录失败: %v", err)
	   	}

	   	var subtitleContent strings.Builder
	   	var subtitleIndex int = 1
	   	var currentStartMs int64 = 0
	   	var currentEndMs int64 = 0
	   	var currentText strings.Builder

	   	for _, word := range words {
	   		if currentStartMs == 0 {
	   			currentStartMs = word.OffsetStartMs
	   		}

	   		// 累加字幕文本
	   		if currentText.Len() > 0 {
	   			currentText.WriteString(" ")
	   		}
	   		currentText.WriteString(word.Word)

	   		// 更新当前字幕的结束时间
	   		currentEndMs = word.OffsetEndMs

	   		// 如果当前字幕片段已经超过5秒，或者是最后一个词，就写入到文件中
	   		if currentEndMs-currentStartMs >= maxDurationMs || &word == &words[len(words)-1] {
	   			subtitleContent.WriteString(fmt.Sprintf("%d\n%s --> %s\n%s\n\n", subtitleIndex, convertMsToSrtTime(currentStartMs), convertMsToSrtTime(currentEndMs), currentText.String()))
	   			subtitleIndex++
	   			// 重置当前字幕片段的开始时间和文本
	   			currentStartMs = 0
	   			currentText.Reset()
	   		}
	   	}  */
	//planB，全，但很抽象
	currentTime := time.Now()

	subtitleDir := mediaService.makeFaceDir("subTitle")
	subtitleFileName := fmt.Sprintf("combined_subtitle_%s.srt", currentTime.Format("20060102_150405"))

	// 创建subTitle目录（如果它还不存在）
	fullPath := path.Join(subtitleDir, subtitleFileName)

	if err := os.MkdirAll(subtitleDir, 0755); err != nil {
		return "", fmt.Errorf("创建字幕目录失败: %v", err)
	}

	var subtitleContent strings.Builder
	var subtitleIndex int = 1

	for _, word := range words {
		startTime := convertMsToSrtTime(word.OffsetStartMs)
		endTime := convertMsToSrtTime(word.OffsetEndMs)
		subtitleContent.WriteString(fmt.Sprintf("%d\n%s --> %s\n%s\n\n", subtitleIndex, startTime, endTime, word.Word))
		subtitleIndex++
	}

	// 将字幕内容写入文件
	err := os.WriteFile(fullPath, []byte(subtitleContent.String()), 0644)
	if err != nil {
		return "", fmt.Errorf("写入文件时发生错误: %v", err)
	}

	// 读取刚刚写入的字幕文件
	subtitleData, err := os.ReadFile(fullPath)
	if err != nil {
		return "", fmt.Errorf("读取字幕文件失败: %v", err)
	}

	// 获取存储系统的实例
	disk := global.App.Disk()

	// 定义存储系统中的文件路径
	storageKey := mediaService.makeFaceDir("subTitle") + "/" + subtitleFileName

	// 上传字幕文件到存储系统
	err = disk.Put("public"+"/"+storageKey, bytes.NewReader(subtitleData), int64(len(subtitleData)))
	if err != nil {
		return "", fmt.Errorf("上传字幕文件失败: %v", err)
	}

	// 删除本地的字幕文件
	if err := os.Remove(fullPath); err != nil {
		log.Printf("删除本地字幕文件失败: %v", err)
	}

	// 返回存储系统中的文件路径或URL
	return disk.Url(storageKey), nil
}

// convertMsToSrtTime 将毫秒转换为srt格式的时间码
func convertMsToSrtTime(ms int64) string {
	hours := ms / 3600000
	minutes := (ms % 3600000) / 60000
	seconds := (ms % 60000) / 1000
	milliseconds := ms % 1000

	return fmt.Sprintf("%02d:%02d:%02d,%03d", hours, minutes, seconds, milliseconds)
}
